#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
ESP32服务端模拟器
实现双向TLS认证的TCP服务器
"""

import socket
import ssl
import threading
import time
import json
from datetime import datetime
from pathlib import Path

class ESP32Server:
    def __init__(self, host='localhost', port=8443):
        self.host = host
        self.port = port
        self.running = False
        self.clients = []
        
        # 证书文件路径
        self.cert_file = 'certs/server-cert.pem'
        self.key_file = 'certs/server-key.pem'
        self.ca_file = 'certs/ca-cert.pem'
        
        # 检查证书文件
        self._check_certificates()
        
    def _check_certificates(self):
        """检查证书文件是否存在"""
        required_files = [self.cert_file, self.key_file, self.ca_file]
        for file_path in required_files:
            if not Path(file_path).exists():
                raise FileNotFoundError(f"证书文件不存在: {file_path}")
        print("✓ 证书文件检查通过")
    
    def _create_ssl_context(self):
        """创建SSL上下文"""
        # 创建SSL上下文
        context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
        
        # 加载服务器证书和私钥
        context.load_cert_chain(self.cert_file, self.key_file)
        
        # 设置客户端证书验证
        context.verify_mode = ssl.CERT_REQUIRED
        context.load_verify_locations(self.ca_file)
        
        # 设置加密套件
        context.set_ciphers('ECDHE+AESGCM:ECDHE+CHACHA20:DHE+AESGCM:DHE+CHACHA20:!aNULL:!MD5:!DSS')
        
        return context
    
    def _handle_client(self, client_socket, client_address):
        """处理客户端连接"""
        client_info = f"{client_address[0]}:{client_address[1]}"
        print(f"[{datetime.now().strftime('%H:%M:%S')}] 客户端连接: {client_info}")
        
        try:
            # 获取客户端证书信息
            cert = client_socket.getpeercert()
            if cert:
                subject = dict(x[0] for x in cert['subject'])
                print(f"[{datetime.now().strftime('%H:%M:%S')}] 客户端证书: {subject.get('commonName', 'Unknown')}")
            
            # 发送欢迎消息
            welcome_msg = {
                'type': 'welcome',
                'message': 'ESP32服务器连接成功',
                'timestamp': datetime.now().isoformat(),
                'server_info': {
                    'device': 'ESP32',
                    'firmware': 'v1.0.0',
                    'capabilities': ['sensor_data', 'control', 'status']
                }
            }
            self._send_message(client_socket, welcome_msg)
            
            # 启动传感器数据发送线程
            sensor_thread = threading.Thread(
                target=self._send_sensor_data, 
                args=(client_socket, client_info),
                daemon=True
            )
            sensor_thread.start()
            
            # 处理客户端消息
            while self.running:
                try:
                    data = client_socket.recv(1024)
                    if not data:
                        break
                    
                    message = data.decode('utf-8')
                    print(f"[{datetime.now().strftime('%H:%M:%S')}] 收到来自 {client_info}: {message}")
                    
                    # 解析JSON消息
                    try:
                        msg_data = json.loads(message)
                        response = self._process_message(msg_data)
                        if response:
                            self._send_message(client_socket, response)
                    except json.JSONDecodeError:
                        # 处理普通文本消息
                        response = {
                            'type': 'echo',
                            'original_message': message,
                            'response': f"ESP32收到: {message}",
                            'timestamp': datetime.now().isoformat()
                        }
                        self._send_message(client_socket, response)
                        
                except socket.timeout:
                    continue
                except Exception as e:
                    print(f"[{datetime.now().strftime('%H:%M:%S')}] 处理客户端消息错误: {e}")
                    break
                    
        except Exception as e:
            print(f"[{datetime.now().strftime('%H:%M:%S')}] 客户端处理错误: {e}")
        finally:
            try:
                client_socket.close()
                if client_socket in self.clients:
                    self.clients.remove(client_socket)
                print(f"[{datetime.now().strftime('%H:%M:%S')}] 客户端断开: {client_info}")
            except:
                pass
    
    def _send_message(self, client_socket, message):
        """发送消息到客户端"""
        try:
            if isinstance(message, dict):
                message = json.dumps(message, ensure_ascii=False)
            client_socket.send(message.encode('utf-8'))
        except Exception as e:
            print(f"[{datetime.now().strftime('%H:%M:%S')}] 发送消息失败: {e}")
    
    def _process_message(self, msg_data):
        """处理客户端消息"""
        msg_type = msg_data.get('type', 'unknown')
        
        if msg_type == 'get_status':
            return {
                'type': 'status_response',
                'status': 'online',
                'uptime': time.time(),
                'memory_free': 45000,
                'wifi_signal': -45,
                'timestamp': datetime.now().isoformat()
            }
        
        elif msg_type == 'control':
            action = msg_data.get('action')
            if action == 'led_on':
                return {
                    'type': 'control_response',
                    'action': 'led_on',
                    'result': 'success',
                    'message': 'LED已开启',
                    'timestamp': datetime.now().isoformat()
                }
            elif action == 'led_off':
                return {
                    'type': 'control_response',
                    'action': 'led_off',
                    'result': 'success',
                    'message': 'LED已关闭',
                    'timestamp': datetime.now().isoformat()
                }
        
        elif msg_type == 'ping':
            return {
                'type': 'pong',
                'timestamp': datetime.now().isoformat()
            }
        
        return {
            'type': 'error',
            'message': f'未知消息类型: {msg_type}',
            'timestamp': datetime.now().isoformat()
        }
    
    def _send_sensor_data(self, client_socket, client_info):
        """定期发送传感器数据"""
        import random
        
        while self.running and client_socket in self.clients:
            try:
                sensor_data = {
                    'type': 'sensor_data',
                    'data': {
                        'temperature': round(random.uniform(20.0, 30.0), 2),
                        'humidity': round(random.uniform(40.0, 80.0), 2),
                        'pressure': round(random.uniform(1000.0, 1020.0), 2),
                        'light': random.randint(0, 1023)
                    },
                    'timestamp': datetime.now().isoformat()
                }
                
                self._send_message(client_socket, sensor_data)
                time.sleep(5)  # 每5秒发送一次传感器数据
                
            except Exception as e:
                print(f"[{datetime.now().strftime('%H:%M:%S')}] 发送传感器数据失败: {e}")
                break
    
    def start(self):
        """启动服务器"""
        try:
            # 创建SSL上下文
            ssl_context = self._create_ssl_context()
            
            # 创建socket
            server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            server_socket.bind((self.host, self.port))
            server_socket.listen(5)
            
            self.running = True
            print(f"[{datetime.now().strftime('%H:%M:%S')}] ESP32服务器启动成功")
            print(f"[{datetime.now().strftime('%H:%M:%S')}] 监听地址: {self.host}:{self.port}")
            print(f"[{datetime.now().strftime('%H:%M:%S')}] 等待客户端连接...")
            
            while self.running:
                try:
                    client_socket, client_address = server_socket.accept()
                    
                    # 包装为SSL socket
                    ssl_client_socket = ssl_context.wrap_socket(
                        client_socket, 
                        server_side=True
                    )
                    ssl_client_socket.settimeout(1.0)
                    
                    self.clients.append(ssl_client_socket)
                    
                    # 创建线程处理客户端
                    client_thread = threading.Thread(
                        target=self._handle_client,
                        args=(ssl_client_socket, client_address),
                        daemon=True
                    )
                    client_thread.start()
                    
                except socket.timeout:
                    continue
                except Exception as e:
                    if self.running:
                        print(f"[{datetime.now().strftime('%H:%M:%S')}] 接受连接错误: {e}")
                    
        except Exception as e:
            print(f"[{datetime.now().strftime('%H:%M:%S')}] 服务器启动失败: {e}")
        finally:
            self.stop()
    
    def stop(self):
        """停止服务器"""
        print(f"[{datetime.now().strftime('%H:%M:%S')}] 正在停止服务器...")
        self.running = False
        
        # 关闭所有客户端连接
        for client in self.clients[:]:
            try:
                client.close()
            except:
                pass
        self.clients.clear()
        
        print(f"[{datetime.now().strftime('%H:%M:%S')}] 服务器已停止")

if __name__ == "__main__":
    server = ESP32Server()
    
    try:
        server.start()
    except KeyboardInterrupt:
        print("\n收到中断信号")
    finally:
        server.stop()